/**TODO: 从js迁移，很多ts类型待完善 */
import {
  useState, useRef, useMemo,
} from 'react';
import Websocket from 'components/websocket';
import { Socket } from './index';
import { MessageType, RequestType } from './enum';
import { getTopicList } from './static';

/** 前端自收发ws */
export default function SocketProvider({ children }: any) {
  const $refWs = useRef<any>(null);

  const [value, setValue] = useState<any>({
    data: {},
    open: () => $refWs?.current?.reconnect(),
    close: () => $refWs?.current?.close(),
    subscribe: () => { },
    cancelSub: () => { },
    publish: () => { },
  });

  const [roomMap, setRoomMap] = useState<any>({}); // 房间表信息
  const [topicList, setTopicList] = useState<any>([]); // 断线前的订阅列表
  const [onMessageFnMap, setOnMessageFnMap] = useState<any>({}); // 订阅时传入回调表
  const [openStatus, setOpenStatus] = useState<any>(false);

  const SocketVal = useMemo(() => ({
    ...value,
    roomMap,
    openStatus,
  }), [value, roomMap, openStatus]);

  /** 处理收到消息 */
  const socketReceiveMsg = (event: any) => {
    try {
      if (event.data === 'pong') return;
      const data = JSON.parse(event.data || '{}');
      const { topicKey, content, messageType } = data;
      /** 订阅时传入回调处理 */
      onMessageFnMap[topicKey]?.forEach((onMessage: any) => onMessage?.(content));
      // 事件监听器
      if (content.event) {
        // handleReceiveEvent(data);
      }
      /** 业务码拦截器 */
      switch (messageType) {
        case MessageType.join:
        case MessageType.leave:
          // handleRoomChange(data, setRoomMap); // 代表该消息为房间内人员变动信息
          return;
        default:
          break;
      }

      setValue((preValue: any) => ({
        ...preValue,
        data: {
          ...preValue.data,
          [topicKey]: content,
        },
      }));
      if (!openStatus) setOpenStatus(true);
    } catch (e) {
      console.error(e);
    }
  };

  /** 发送消息 */
  const sendMsg = (requestType: any, baseContent: any) => {
    const sendData = { requestType, baseContent };
    $refWs.current?.send?.(JSON.stringify(sendData));
  };

  /** 订阅话题 */
  const subscribe = (
    topicKeyList: any,
    onMessage: any,
    forceSub: any, // 是否强制订阅 (忽略对重复订阅的过滤)
  ) => {
    sendMsg(RequestType.subscribe, { topicKeyList });
    setOnMessageFnMap((pre: any) => {
      topicKeyList.forEach(({ topicKey }: any) => {
        if (!pre[topicKey]) pre[topicKey] = [];
        pre[topicKey].push(onMessage);
      });
      return { ...pre };
    });
    const { newTopicList, deltaTopicList } = getTopicList(
      topicList,
      topicKeyList,
    );
    if (deltaTopicList.length || forceSub) setTopicList(newTopicList);
  };

  /** 取消订阅话题 */
  const cancelSub = (topicKeyList: any) => {
    const { newTopicList, deltaTopicList } = getTopicList(
      topicList,
      topicKeyList,
      true,
    );
    if (!deltaTopicList.length) return; // 过滤重复操作
    sendMsg(RequestType.cancelSub, { topicKeyList });
    setTopicList(newTopicList);
    setRoomMap((pre: any) => {
      const tempRoomMap = { ...pre };
      deltaTopicList
        .filter((d) => d.room)
        .forEach((topicKeyDto) => {
          delete tempRoomMap[topicKeyDto.topicKey];
        });
      return tempRoomMap;
    });
    setOnMessageFnMap((pre: any) => {
      deltaTopicList.forEach(({ topicKey }) => {
        delete pre[topicKey];
      });
      return { ...pre };
    });
  };

  /** 推送消息到话题列表 */
  const publish = (topicKeyList: any, messageContent: any) => {
    sendMsg(RequestType.publish, { topicKeyList, messageContent });
  };

  const clear = () => {
    setRoomMap({});
    setTopicList([]);
    setValue((pre: any) => ({ ...pre, data: {} }));
  };

  /** 获取实例 */
  const getWSInstance = () => {
    const wsInstance = $refWs?.current;
    setValue((pre: any) => ({
      ...pre,
      open: () => wsInstance?.reconnect(),
      close: (logout: any) => {
        wsInstance?.close();
        if (logout) clear();
      },
      subscribe,
      cancelSub,
      publish,
    }));
  };

  /** 重新连接ws 重新订阅话题 获取用户列表 */
  const handleReOpen = () => {
    getWSInstance();
    if (topicList.length) subscribe(topicList, () => { }, true);
    setOpenStatus(true);
  };

  return (
    <Socket.Provider value={SocketVal}>
      {children}
      <Websocket
        ref={$refWs}
        socketReceiveMsg={socketReceiveMsg}
        onOpen={handleReOpen}
        onClose={() => setOpenStatus(false)}
      />
    </Socket.Provider>
  );
}
